import abc
import logging
from datetime import datetime
from typing import List
import json

from .DownloadInfo import DownloadInfo


class Meta:
    """
    一个记录元数据的类
    包括应该从何处下载元数据(download_info)和下载到的元数据(meta_data)
    """

    def __init__(self, download_info: DownloadInfo = None, meta_data=None):
        self.download_info = download_info
        self.meta_data = meta_data

    def serialize(self) -> dict:
        data = {}
        if self.download_info is not None:
            data['download_info'] = self.download_info.serialize()
        if self.meta_data is not None:
            data['meta_data'] = self.meta_data
        return data

    def deserialize(self, data: dict):
        if 'download_info' in data:
            self.download_info = DownloadInfo('', '', None)
            self.download_info.deserialize(data['download_info'])
        else:
            self.download_info = None
        if 'meta_data' in data:
            self.meta_data = data['meta_data']
        else:
            self.meta_data = None

    def __str__(self):
        return json.dumps(self.serialize())

    @staticmethod
    def example():
        return Meta(DownloadInfo(), "spider会把你定义的driver.get_meta所爬到的元数据放在这里")


class MetaList:
    """
    一个记录元数据的列表
    因为每个相册可能需要从多个地方收集元数据
    """

    def __init__(self, meta_list: List[Meta]):
        """
        初始化MetaDownloadInfo
        :param meta_list: 元数据下载信息列表
        """
        self.meta_list = meta_list

    def serialize(self) -> list:
        """
        将MetaDownloadInfo组织为一个list
        :return: MetaDownloadInfo对应的list
        """
        return [meta.serialize() for meta in self.meta_list]

    def deserialize(self, data: list):
        self.meta_list = []
        for d in data:
            meta = Meta(None, None)
            meta.deserialize(d)
            self.meta_list.append(meta)

    @staticmethod
    def example():
        return MetaList([Meta.example(), Meta.example(), Meta.example()])

    def __str__(self):
        return json.dumps(self.serialize())


class MetaSpiderDriver(metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def type_name(self) -> str:
        """
        此Driver的类型名称，用于标识不同的Driver
        :return: 类型名称字符串
        """
        pass

    @abc.abstractmethod  # 定义获取元数据的抽象方法
    def download_meta(self, meta: Meta):
        """
        获取元数据
        :param meta: meta.download_info记录了下载元数据所需的信息，元数据应该放入meta.meta_data中
        """
        pass


class MetaSpider:
    """
    下载并保存元数据
    """

    def __init__(self):
        self.drivers = {}

    def add_driver(self, driver: MetaSpiderDriver):
        """
        添加Driver
        :param driver: 要添加的MetaSpiderDriver
        """
        t = driver.type_name()
        if t in self.drivers:
            logging.warning("MetaSpiderDriver %s 已存在" % t)
        self.drivers[t] = driver
        logging.info("MetaSpiderDriver %s 已添加" % t)

    def download_meta(self, meta_list: MetaList):
        """
        把元数据下载到meta_list里面
        :param meta_list: 这个变量记录了下载所需的信息
        """
        for meta in meta_list.meta_list:
            download_info = meta.download_info
            type_name = download_info.driver_type_name
            if type_name in self.drivers:
                logging.info("让 MetaSpiderDriver %s   处理 download_info %s" %
                             (type_name, download_info.serialize()))
                try:
                    self.drivers[type_name].download_meta(meta)
                    download_info.last_updated_time = datetime.now()
                    logging.info("   MetaSpiderDriver %s 已下载 download_info %s 对应元数据 %s" %
                                 (type_name, download_info.serialize(), str(meta.meta_data)))
                except Exception as e:
                    logging.error("MetaSpiderDriver %s 下载 download_info %s 对应元数据时发生错误: %s" %
                                  (type_name, download_info.serialize(), str(e)))
            else:
                logging.warning("找不到 type_name = %s 的 MetaSpiderDriver" % type_name)
